package com.itbaizhan.shopping_search_service.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itbaizhan.shopping_common.pojo.*;
import com.itbaizhan.shopping_common.service.SearchService;
import com.itbaizhan.shopping_search_service.repository.GoodsESRepository;
import io.netty.util.internal.StringUtil;
import lombok.SneakyThrows;

import org.apache.dubbo.config.annotation.Service;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.AnalyzeRequest;
import org.elasticsearch.client.indices.AnalyzeResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.SuggestBuilders;
import org.elasticsearch.search.suggest.SuggestionBuilder;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author spf
 * @date 2023/12/30
 * @time 19:17
 */

// 搜索服务实现类
//@Service
@Service
@Component
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;
    @Autowired
    private GoodsESRepository goodsESRepository;
    @Autowired
    private ElasticsearchRestTemplate template;

    // 监听同步商品队列
    @RabbitListener(queues = "sync_goods_queue")
    public void listenSyncQueue(GoodsDesc goodsDesc){
        syncGoodsToES(goodsDesc);
    }
    // 监听删除队列
    @RabbitListener(queues = "del_goods_queue")
    public void listenDelQueue(Long id){
        delete(id);
    }

    /**
     * 分词
     *
     * @param text     被分词文本
     * @param analyzer 分词器
     * @return 分词结果
     */
    @SneakyThrows
    public List<String> analyze(String text, String analyzer) {
        // 分词请求
        AnalyzeRequest request = AnalyzeRequest.withIndexAnalyzer("goods", analyzer, text);
        // 发送分词请求，获取相应结果
        AnalyzeResponse response = restHighLevelClient.indices().analyze(request, RequestOptions.DEFAULT);
        // 处理相应结果
        List<String> words = new ArrayList<>();
        List<AnalyzeResponse.AnalyzeToken> tokens = response.getTokens();
        for (AnalyzeResponse.AnalyzeToken token : tokens) {
            words.add(token.getTerm());
        }
        return words;
    }

    /**
     * 自动补齐
     *
     * @param keyword 输入关键字
     * @return 补齐关键字列表
     */
    @Override
    public List<String> autoSuggest(String keyword) {
        // 构造补全条件
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        SuggestionBuilder suggestionBuilder = SuggestBuilders
                .completionSuggestion("tags") // 补全的词出自哪一列
                .skipDuplicates(true) // 是否去重
                .prefix(keyword) // 关键词前缀
                .size(10);// 最多显示不数据
        suggestBuilder.addSuggestion("prefix_suggestion", suggestionBuilder);
        // 发送请求
        SearchResponse response = template.suggest(suggestBuilder, IndexCoordinates.of("goods"));
        // 处理响应结果
        List<String> stringList = response.getSuggest()
                .getSuggestion("prefix_suggestion")
                .getEntries()
                .get(0)
                .getOptions()
                .stream()
                .map(Suggest.Suggestion.Entry.Option::getText)
                .map(Text::toString)
                .collect(Collectors.toList());
        return stringList;
    }

    /**
     * 商品搜索
     *
     * @param goodsSearchParam 商品搜索参数
     * @return 执行结果
     */
    @Override
    public GoodsSearchResult search(GoodsSearchParam goodsSearchParam) {
        // 1.构造搜索条件
        NativeSearchQuery query = queryBuild(goodsSearchParam);
        // 2.搜索
        SearchHits<GoodsES> search = template.search(query, GoodsES.class);
        // 3.处理结果
        GoodsSearchResult goodsSearchResult = new GoodsSearchResult();
        // 封装为page对象
        List<GoodsES> content = new ArrayList<>();
        for (SearchHit<GoodsES> goodsESSearchHit : search) {
            GoodsES goodsES = goodsESSearchHit.getContent();
            content.add(goodsES);
        }
        Page<GoodsES> page = new Page<>();
        page.setCurrent(goodsSearchParam.getPage())
                .setSize(goodsSearchParam.getSize())
                .setTotal(search.getTotalHits())
                .setRecords(content);
        goodsSearchResult.setGoodsPage(page);
        // 封装成搜索条件用于回显
        goodsSearchResult.setGoodsSearchParam(goodsSearchParam);
        // 封装查询面板,即根据查询条件，找到查询结果关联度前20名的商品进行封装
        buildSearchPanel(goodsSearchParam, goodsSearchResult);
        return goodsSearchResult;
    }

    /**
     * 封装查询面板,即根据查询条件，找到查询结果关联度前20名的商品进行封装
     *
     * @param goodsSearchParam  商品搜索参数
     * @param goodsSearchResult 搜索结果
     */
    private void buildSearchPanel(GoodsSearchParam goodsSearchParam, GoodsSearchResult goodsSearchResult) {
        // 构造搜索条件
        goodsSearchParam.setPage(1);
        goodsSearchParam.setSize(20);
        goodsSearchParam.setSort(null);
        goodsSearchParam.setSortFiled(null);
        NativeSearchQuery nativeSearchQuery = queryBuild(goodsSearchParam);
        // 搜索
        SearchHits<GoodsES> search1 = template.search(nativeSearchQuery, GoodsES.class);
        // 处理结果
        List<GoodsES> content = new ArrayList<>();
        for (SearchHit<GoodsES> goodsESSearchHit : search1) {
            GoodsES goodsES = goodsESSearchHit.getContent();
            content.add(goodsES);
        }
        // 遍历集合，封装查询面板
        Set<String> brands = new HashSet<>();
        Set<String> productTypes = new HashSet<>();
        HashMap<String, Set<String>> specifications = new HashMap<>();
        for (GoodsES goodsES : content) {
            brands.add(goodsES.getBrand());
            productTypes.addAll(goodsES.getProductType());
            Map<String, List<String>> specification = goodsES.getSpecification();
            Set<Map.Entry<String, List<String>>> entries = specification.entrySet();
            for (Map.Entry<String, List<String>> entry : entries) {
                String key = entry.getKey();
                List<String> value = entry.getValue();
                if (!specifications.containsKey(key)) {
                    specifications.put(key, new HashSet<>(value));
                } else {
                    specifications.get(key).addAll(value);
                }
            }
        }

        goodsSearchResult.setBrands(brands);
        goodsSearchResult.setProductType(productTypes);
        goodsSearchResult.setSpecifications(specifications);
    }

    /**
     * 构造搜索条件
     *
     * @param goodsSearchParam 商品搜索参数
     */
    private NativeSearchQuery queryBuild(GoodsSearchParam goodsSearchParam) {
        // 构造复杂查询条件
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 如果查询条件有关键词，关键词可以匹配商品名、副标题、品牌字段，否则查询所有商品
        if (!StringUtil.isNullOrEmpty(goodsSearchParam.getKeyword())) {
            String keyword = goodsSearchParam.getKeyword();
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "goodsName", "caption", "brand");
            queryBuilder.must(multiMatchQueryBuilder);
        } else {
            MatchAllQueryBuilder matchAllQuery = QueryBuilders.matchAllQuery();
            queryBuilder.must(matchAllQuery);
        }
        // 如果查询条件有品牌，则精准匹配品牌
        String brand = goodsSearchParam.getBrand();
        if (!StringUtil.isNullOrEmpty(brand)) {
            QueryBuilders.termQuery("brand", brand);
        }
        // 如果查询条件有价格，则匹配价格
        Double highPrice = goodsSearchParam.getHighPrice();
        Double lowPrice = goodsSearchParam.getLowPrice();
        if (highPrice != null && highPrice != 0) {
            RangeQueryBuilder lte = QueryBuilders.rangeQuery("price").lte(highPrice);
            queryBuilder.must(lte);
        }
        if (lowPrice != null && lowPrice != 0) {
            RangeQueryBuilder gte = QueryBuilders.rangeQuery("price").gte(lowPrice);
            queryBuilder.must(gte);
        }
        // 如果查询条件有规格项，则精准匹配规格项
        Map<String, String> specificationOption = goodsSearchParam.getSpecificationOption();
        if (specificationOption != null && specificationOption.size() > 0) {
            Set<Map.Entry<String, String>> entries = specificationOption.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
                if (!StringUtil.isNullOrEmpty(key)) {
                    TermQueryBuilder termQuery = QueryBuilders.termQuery("specification." + key + ".keyword", value);
                    queryBuilder.must(termQuery);
                }
            }
        }

        // 添加分页条件
        PageRequest pageRequest = PageRequest.of(goodsSearchParam.getPage() - 1, goodsSearchParam.getSize());
        // 查询构造器
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        nativeSearchQueryBuilder.withQuery(queryBuilder).withPageable(pageRequest);

        // 如果查询条件有排序，则添加排序条件
        String sortFiled = goodsSearchParam.getSortFiled();
        String sort = goodsSearchParam.getSort();
        SortBuilder sortBuilder = null;
        if (!StringUtil.isNullOrEmpty(sortFiled)) {
            if (sortFiled.equals("NEW")) {
                sortBuilder = SortBuilders.fieldSort("id");
                if (sort.equals("ASC")) {
                    sortBuilder.order(SortOrder.DESC);
                }
                if (sort.equals("DESC")) {
                    sortBuilder.order(SortOrder.ASC);
                }
            }
            if (sortFiled.equals("PRICE")) {
                sortBuilder = SortBuilders.fieldSort("price");
                if (sort.equals("ASC")) {
                    sortBuilder.order(SortOrder.ASC);
                }
                if (sort.equals("DESC")) {
                    sortBuilder.order(SortOrder.DESC);
                }
            }
            nativeSearchQueryBuilder.withSort(sortBuilder);
        }


        // 返回查询条件对象
        NativeSearchQuery query = nativeSearchQueryBuilder.build();
        return query;
    }


    /**
     * 向es中同步商品数据
     *
     * @param goodsDesc 商品详情对象
     */
    @Override
    public void syncGoodsToES(GoodsDesc goodsDesc) {
        // 将商品详情对象转为goodses对象
        GoodsES goodsES = new GoodsES();
        goodsES.setId(goodsDesc.getId());
        goodsES.setGoodsName(goodsDesc.getGoodsName());
        goodsES.setCaption(goodsDesc.getCaption());
        goodsES.setPrice(goodsDesc.getPrice());
        goodsES.setHeaderPic(goodsDesc.getHeaderPic());
        goodsES.setBrand(goodsDesc.getBrand().getName());
        // 类型集合
        List<String> productType = new ArrayList<>();
        productType.add(goodsDesc.getProductType1().getName());
        productType.add(goodsDesc.getProductType2().getName());
        productType.add(goodsDesc.getProductType3().getName());
        goodsES.setProductType(productType);
        // 规格集合
        HashMap<String, List<String>> map = new HashMap<>();
        List<Specification> specifications = goodsDesc.getSpecifications();
        for (Specification specification : specifications) {
            // 规格项集合
            List<SpecificationOption> specificationOptions = specification.getSpecificationOptions();
            List<String> optionList = new ArrayList<>();
            for (SpecificationOption specificationOption : specificationOptions) {
                optionList.add(specificationOption.getOptionName());
            }
            map.put(specification.getSpecName(), optionList);
        }
        goodsES.setSpecification(map);
        // 关键字集合
        List<String> tags = new ArrayList<>();
        tags.add(goodsDesc.getBrand().getName());
        tags.addAll(analyze(goodsDesc.getGoodsName(), "ik_smart"));
        tags.addAll(analyze(goodsDesc.getCaption(), "ik_smart"));
        goodsES.setTags(tags);

        goodsESRepository.save(goodsES);
    }

    @Override
    public void delete(Long id) {
        goodsESRepository.deleteById(id);
    }
}
